<?php
/**
 * 腾讯云直播服务类
 * 包含直播推流和直播播放功能
 * 适用于PHP 5.6版本
 * https://cloud.tencent.com/document/product/267/32732
 */
class TencentLiveService {
    /**
     * 腾讯云直播配置信息
     */
    private $config = array(
        'appId'      => '',  // 腾讯云直播AppID
        'secretId'   => '',  // 腾讯云API密钥SecretId
        'secretKey'  => '',  // 腾讯云API密钥SecretKey
        'pushDomain' => '',  // 推流域名
        'playDomain' => '',  // 播放域名
        'pushKey'    => '',  // 推流鉴权key
        'playKey'    => '',  // 播放鉴权key
        'pushExpire' => 86400,  // 推流地址有效期(秒)
        'playExpire' => 86400,  // 播放地址有效期(秒)
    );

    /**
     * 构造函数
     * @param array $config 配置信息
     */
    public function __construct($config = array()) {
        if (!empty($config)) {
            $this->config = array_merge($this->config, $config);
        }
    }

    /**
     * 生成推流地址
     * @param string $streamName 流名称
     * @param string $format 推流格式，支持 rtmp、webrtc
     * @param int $expire 有效期(秒)，默认使用配置中的值
     * @return string 推流地址
     */
    public function getPushUrl($streamName, $format = 'rtmp', $expire = null) {
        if (empty($streamName)) {
            return false;
        }

        $expire = $expire ?: $this->config['pushExpire'];
        $domain = $this->config['pushDomain'];
        $key = $this->config['pushKey'];

        // 根据格式确定协议和路径
        $protocol = '';
        $path = '';
        switch (strtolower($format)) {
            case 'rtmp':
                $protocol = 'rtmp://';
                $path = '/live/';
                break;
            case 'webrtc':
                $protocol = 'webrtc://';
                $path = '/live/';
                break;
            default:
                $protocol = 'rtmp://';
                $path = '/live/';
                break;
        }

        // 无鉴权模式
        if (empty($key)) {
            return "{$protocol}{$domain}{$path}{$streamName}";
        }

        // 有鉴权模式
        $txTime = strtoupper(base_convert(time() + $expire, 10, 16));
        $txSecret = md5($key . $streamName . $txTime);
        $ext = "?txSecret={$txSecret}&txTime={$txTime}";

        return "{$protocol}{$domain}{$path}{$streamName}{$ext}";
    }

    /**
     * 生成播放地址
     * @param string $streamName 流名称
     * @param string $format 播放格式，支持 rtmp、flv、hls、udp、webrtc
     * @param int $expire 有效期(秒)，默认使用配置中的值
     * @return string 播放地址
     */
    public function getPlayUrl($streamName, $format = 'flv', $expire = null) {
        if (empty($streamName)) {
            return false;
        }

        $expire = $expire ?: $this->config['playExpire'];
        $domain = $this->config['playDomain'];
        $key = $this->config['playKey'];

        // 根据格式确定协议和路径
        $protocol = '';
        $path = '';
        switch (strtolower($format)) {
            case 'rtmp':
                $protocol = 'rtmp://';
                $path = '/live/';
                break;
            case 'flv':
                $protocol = 'http://';
                $path = '/live/';
                $streamName .= '.flv';
                break;
            case 'hls':
                $protocol = 'http://';
                $path = '/live/';
                $streamName .= '.m3u8';
                break;
            case 'udp':
                $protocol = 'webrtc://';
                $path = '/live/';
                break;
            case 'webrtc':
                $protocol = 'webrtc://';
                $path = '/live/';
                break;
            default:
                return false;
        }

        // 无鉴权模式
        if (empty($key)) {
            return "{$protocol}{$domain}{$path}{$streamName}";
        }

        // 有鉴权模式
        $txTime = strtoupper(base_convert(time() + $expire, 10, 16));
        $txSecret = md5($key . $streamName . $txTime);
        $ext = "?txSecret={$txSecret}&txTime={$txTime}";

        return "{$protocol}{$domain}{$path}{$streamName}{$ext}";
    }

    /**
     * 生成直播录制任务
     * @param string $streamName 流名称
     * @param string $fileName 录制文件名
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return array 录制任务结果
     */
    public function createRecordTask($streamName, $fileName, $startTime, $endTime) {
        // 构建请求参数
        $params = array(
            'Action'     => 'CreateRecordTask',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'StreamName' => $streamName,
            'AppName'    => 'live',
            'DomainName' => $this->config['pushDomain'],
            'StartTime'  => $startTime,
            'EndTime'    => $endTime,
            'FileName'   => $fileName,
            'FileFormat' => 'mp4',
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 查询直播流状态
     * @param string $streamName 流名称
     * @return array 流状态信息
     */
    public function getStreamStatus($streamName) {
        // 构建请求参数
        $params = array(
            'Action'     => 'DescribeLiveStreamState',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'StreamName' => $streamName,
            'AppName'    => 'live',
            'DomainName' => $this->config['pushDomain'],
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 禁用直播流
     * @param string $streamName 流名称
     * @param int $resumeTime 恢复时间戳，为0表示永久禁用
     * @return array 操作结果
     */
    public function forbidStream($streamName, $resumeTime = 0) {
        // 构建请求参数
        $params = array(
            'Action'     => 'ForbidLiveStream',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'StreamName' => $streamName,
            'AppName'    => 'live',
            'DomainName' => $this->config['pushDomain'],
            'ResumeTime' => $resumeTime,
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 恢复直播流
     * @param string $streamName 流名称
     * @return array 操作结果
     */
    public function resumeStream($streamName) {
        // 构建请求参数
        $params = array(
            'Action'     => 'ResumeLiveStream',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'StreamName' => $streamName,
            'AppName'    => 'live',
            'DomainName' => $this->config['pushDomain'],
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 获取直播流信息列表
     * @param int $pageNum 页码
     * @param int $pageSize 每页数量
     * @return array 直播流列表
     */
    public function getLiveStreamList($pageNum = 1, $pageSize = 20) {
        // 构建请求参数
        $params = array(
            'Action'     => 'DescribeLiveStreamOnlineList',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'DomainName' => $this->config['pushDomain'],
            'AppName'    => 'live',
            'PageNum'    => $pageNum,
            'PageSize'   => $pageSize,
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 获取直播推流历史
     * @param string $streamName 流名称
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return array 推流历史记录
     */
    public function getPushHistory($streamName, $startTime, $endTime) {
        // 构建请求参数
        $params = array(
            'Action'     => 'DescribeLiveStreamPublishedList',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'StreamName' => $streamName,
            'AppName'    => 'live',
            'DomainName' => $this->config['pushDomain'],
            'StartTime'  => $startTime,
            'EndTime'    => $endTime,
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 生成API签名 (腾讯云API 3.0签名)
     * @param array $params 请求参数
     * @return string 签名
     */
    private function generateSign($params) {
        // 1. 准备签名所需参数
        $host = "live.tencentcloudapi.com";
        $algorithm = "TC3-HMAC-SHA256";
        $service = "live";
        $action = $params['Action'];
        $version = $params['Version'];
        $timestamp = $params['Timestamp'];
        $date = gmdate("Y-m-d", $timestamp);
        $secretId = $this->config['secretId'];
        $secretKey = $this->config['secretKey'];
        
        // 2. 创建规范请求串
        ksort($params);
        $canonicalQueryString = "";
        foreach ($params as $key => $value) {
            if ($key != "Signature") {
                $canonicalQueryString .= "&" . $this->percentEncode($key) . "=" . $this->percentEncode($value);
            }
        }
        $canonicalQueryString = substr($canonicalQueryString, 1);
        
        $canonicalHeaders = "content-type:application/json; charset=utf-8
host:" . $host . "
";
        $signedHeaders = "content-type;host";
        $payload = "{}"; // POST请求体，GET请求为空
        $hashedRequestPayload = hash("SHA256", $payload);
        $canonicalRequest = "GET
/
" . $canonicalQueryString . "
" . $canonicalHeaders . "
" . $signedHeaders . "
" . $hashedRequestPayload;
        
        // 3. 创建签名串
        $credentialScope = $date . "/" . $service . "/tc3_request";
        $hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
        $stringToSign = $algorithm . "
" . $timestamp . "
" . $credentialScope . "
" . $hashedCanonicalRequest;
        
        // 4. 计算签名
        $secretDate = hash_hmac("SHA256", $date, "TC3" . $secretKey, true);
        $secretService = hash_hmac("SHA256", $service, $secretDate, true);
        $secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
        $signature = hash_hmac("SHA256", $stringToSign, $secretSigning);
        
        // 5. 构造授权信息
        $authorization = $algorithm . " Credential=" . $secretId . "/" . $credentialScope . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature;
        
        return $authorization;
    }
    
    /**
     * URL编码
     * @param string $str 需要编码的字符串
     * @return string 编码后的字符串
     */
    private function percentEncode($str) {
        $res = urlencode($str);
        $res = str_replace(array('+', '*'), array('%20', '%2A'), $res);
        $res = preg_replace('/%7E/', '~', $res);
        return $res;
    }

    /**
     * 发送API请求
     * @param array $params 请求参数
     * @return array 请求结果
     */
    private function sendRequest($params) {
        // 提取Action
        $action = $params['Action'];
        
        // 公共参数
        $params['Region'] = 'ap-guangzhou';
        $params['Version'] = '2018-08-01';
        $params['Timestamp'] = time();
        $params['SecretId'] = $this->config['secretId'];
        
        // 生成签名
        $params['Signature'] = $this->generateSignatureV1($params);
        
        // 构建请求URL
        $url = 'https://live.tencentcloudapi.com/?' . http_build_query($params);
        
        // 发送请求
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/x-www-form-urlencoded',
            'Host: live.tencentcloudapi.com'
        ));
        
        $response = curl_exec($ch);
        
        // 调试信息
        if (curl_errno($ch)) {
            $error = 'Curl error: ' . curl_error($ch);
            curl_close($ch);
            return array('Error' => $error);
        }
        
        curl_close($ch);
        
        // 解析结果
        return json_decode($response, true);
    }
    
    /**
     * 生成API签名 (腾讯云API旧版签名V1)
     * @param array $params 请求参数
     * @return string 签名
     */
    private function generateSignatureV1($params) {
        // 1. 参数排序
        ksort($params);
        
        // 2. 拼接请求字符串
        $str = '';
        foreach ($params as $key => $value) {
            if ($key === 'Signature') {
                continue;
            }
            $str .= $key . '=' . $value . '&';
        }
        $str = trim($str, '&');
        
        // 3. 拼接签名原文字符串
        $signStr = "GET" . "live.tencentcloudapi.com" . "/?" . $str;
        
        // 4. 生成签名串
        $signature = base64_encode(hash_hmac('sha1', $signStr, $this->config['secretKey'], true));
        
        return $signature;
    }

    /**
     * 生成截图任务
     * @param string $streamName 流名称
     * @return array 截图结果
     */
    public function createScreenshotTask($streamName) {
        // 构建请求参数
        $params = array(
            'Action'     => 'CreateLiveSnapshotTask',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'StreamName' => $streamName,
            'AppName'    => 'live',
            'DomainName' => $this->config['pushDomain'],
            'SnapshotInterval' => 10, // 截图间隔，单位秒
            'CosAppId'   => $this->config['appId'],
            'CosBucket'  => 'livesnap',
            'CosRegion'  => 'ap-guangzhou',
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 生成鉴黄任务
     * @param string $streamName 流名称
     * @return array 鉴黄任务结果
     */
    public function createPornDetectTask($streamName) {
        // 构建请求参数
        $params = array(
            'Action'     => 'CreateLivePornDetectTask',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'StreamName' => $streamName,
            'AppName'    => 'live',
            'DomainName' => $this->config['pushDomain'],
            'PornThreshold' => 80, // 鉴黄阈值，范围0-100
            'Interval'   => 20,    // 检测间隔，单位秒
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 获取直播水印列表
     * @return array 水印列表
     */
    public function getWatermarkList() {
        // 构建请求参数
        $params = array(
            'Action'     => 'DescribeLiveWatermarks',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 添加水印规则
     * @param string $streamName 流名称
     * @param int $watermarkId 水印ID
     * @return array 操作结果
     */
    public function addWatermarkRule($streamName, $watermarkId) {
        // 构建请求参数
        $params = array(
            'Action'     => 'CreateLiveWatermarkRule',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'StreamName' => $streamName,
            'AppName'    => 'live',
            'DomainName' => $this->config['pushDomain'],
            'WatermarkId' => $watermarkId,
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 获取直播回调配置
     * @return array 回调配置
     */
    public function getCallbackConfig() {
        // 构建请求参数
        $params = array(
            'Action'     => 'DescribeLiveCallbackRules',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
        );

        // 发送API请求
        return $this->sendRequest($params);
    }

    /**
     * 设置直播回调地址
     * @param string $callbackUrl 回调地址
     * @param array $callbackEvents 回调事件数组
     * @return array 操作结果
     */
    public function setCallbackUrl($callbackUrl, $callbackEvents = array()) {
        if (empty($callbackEvents)) {
            $callbackEvents = array(
                'stream_push', 'stream_end', 'record_complete', 
                'snapshot_complete', 'porn_detect_complete'
            );
        }

        // 构建请求参数
        $params = array(
            'Action'     => 'CreateLiveCallbackRule',
            'SecretId'   => $this->config['secretId'],
            'Timestamp'  => time(),
            'Nonce'      => rand(10000, 99999),
            'DomainName' => $this->config['pushDomain'],
            'AppName'    => 'live',
            'CallbackUrl' => $callbackUrl,
            'CallbackEvents' => implode(';', $callbackEvents),
        );

        // 发送API请求
        return $this->sendRequest($params);
    }
}